package com.hhc.interview;

import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.*;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.*;
import org.apache.lucene.store.RAMDirectory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LuceneService {
    private final RAMDirectory directory;
    private IndexWriter indexWriter;
    private IndexSearcher indexSearcher;
    private final Lock writeLock;

    public LuceneService() {
        directory = new RAMDirectory();
        writeLock = new ReentrantLock();
        IndexWriterConfig config = new IndexWriterConfig(new StandardAnalyzer());
        try {
            indexWriter = new IndexWriter(directory, config);
        } catch (IOException e) {
            throw new RuntimeException("Failed to initialize IndexWriter", e);
        }
    }

    public void initializeIndex(List<Document> documents) {
        writeLock.lock();
        try {
            indexWriter.addDocuments(documents);
            indexWriter.commit();
            restartIndexSearcher();
        } catch (IOException e) {
            handleException(e);
        } finally {
            writeLock.unlock();
        }
    }

    public void updateDocuments(List<Document> documents) {
        writeLock.lock();
        try {
            for (Document doc : documents) {
                String id = doc.get("id");
                indexWriter.updateDocument(new Term("id", id), doc);
            }
            indexWriter.commit();
            restartIndexSearcher();
        } catch (IOException e) {
            handleException(e);
        } finally {
            writeLock.unlock();
        }
    }

    public List<Document> searchDocuments(int page, int pageSize, String query, List<String> statusList, long startTime, long endTime) {
        Query luceneQuery = new MatchAllDocsQuery();
        if (query != null && !query.isEmpty()) {
            luceneQuery = new TermQuery(new Term("title", query));
        }
        if (statusList != null && !statusList.isEmpty()) {
            BooleanQuery.Builder bq = new BooleanQuery.Builder();
            bq.add(luceneQuery, BooleanClause.Occur.MUST);
            for (String status : statusList) {
                bq.add(new TermQuery(new Term("status", status)), BooleanClause.Occur.SHOULD);
            }
            luceneQuery = bq.build();
        }
        if (startTime > 0 && endTime > 0) {
            BooleanQuery.Builder bq = new BooleanQuery.Builder();
            bq.add(luceneQuery, BooleanClause.Occur.MUST);
            bq.add(LongPoint.newRangeQuery("time", startTime, endTime), BooleanClause.Occur.SHOULD);
            luceneQuery = bq.build();
        }

        Sort sort = new Sort(new SortField("id", SortField.Type.LONG, true));

        List<Document> documentsList = new ArrayList<>();
        try  {
            IndexSearcher searcher = new IndexSearcher(DirectoryReader.open(directory));
            int fromIndex = (page - 1) * pageSize;
            int toIndex = Math.min(fromIndex + pageSize, searcher.getIndexReader().maxDoc());
            TopDocs topDocs = searcher.search(luceneQuery, toIndex, sort);
            for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
                Document doc = searcher.doc(scoreDoc.doc);
                documentsList.add(doc);
            }
        } catch (IOException e) {
            handleException(e);
        }

        return documentsList;
    }

    public long countDocuments(String query, List<String> statusList, long startTime, long endTime) {
        Query luceneQuery = new MatchAllDocsQuery();
        if (query != null && !query.isEmpty()) {
            luceneQuery = new TermQuery(new Term("title", query));
        }
        if (statusList != null && !statusList.isEmpty()) {
            BooleanQuery.Builder bq = new BooleanQuery.Builder();
            bq.add(luceneQuery, BooleanClause.Occur.MUST);
            for (String status : statusList) {
                bq.add(new TermQuery(new Term("status", status)), BooleanClause.Occur.SHOULD);
            }
            luceneQuery = bq.build();
        }
        if (startTime > 0 && endTime > 0) {
            BooleanQuery.Builder bq = new BooleanQuery.Builder();
            bq.add(luceneQuery, BooleanClause.Occur.MUST);
            bq.add(LongPoint.newRangeQuery("time", startTime, endTime), BooleanClause.Occur.SHOULD);
            luceneQuery = bq.build();
        }

        long count = 0;
        try  {
            IndexSearcher searcher = new IndexSearcher(DirectoryReader.open(directory));
            count = searcher.count(luceneQuery);
        } catch (IOException e) {
            handleException(e);
        }

        return count;
    }

    private void restartIndexSearcher() {
        try {
            indexSearcher = new IndexSearcher(DirectoryReader.open(directory));
        } catch (IOException e) {
            handleException(e);
        }
    }

    private void handleException(IOException e) {
        System.err.println("An error occurred: " + e.getMessage());
    }

}